﻿#region

using System;
using System.Timers;

#endregion

namespace GGEditor.API.Utility.Forms
{
    public interface IStatusTracker
    {
        event StatusTextEventHandler StatusChanged;
    }

    public delegate void StatusTextEventHandler(string text);

    public class StatusTracker : IStatusTracker
    {
        private double defaultDuration = 5.0;
        private string defaultStatusText = "";
        private IState state;

        public StatusTracker()
        {
            Transition(null, new DefaultStatus(this));
        }

        public StatusTracker(string defaultStatusText) : this()
        {
            DefaultStatusText = defaultStatusText;
        }

        public StatusTracker(string defaultStatusText, double defaultDuration)
            : this(defaultStatusText)
        {
            DefaultDuration = defaultDuration;
        }

        public string DefaultStatusText
        {
            get { return defaultStatusText; }
            set
            {
                lock (this) {
                    if (value == DefaultStatusText)
                        return;
                    defaultStatusText = value;
                }
                if (state is DefaultStatus)
                    OnStatusChanged();
            }
        }

        public string StatusText
        {
            get
            {
                lock (this) {
                    return state.StatusText;
                }
            }
        }

        public double DefaultDuration
        {
            get { return defaultDuration; }
            set
            {
                lock (this)
                    defaultDuration = Math.Max(0, value);
            }
        }

        #region IStatusTracker Members

        public event StatusTextEventHandler StatusChanged
        {
            add
            {
                lock (this) {
                    statusChanged += value;
                    value(StatusText);
                }
            }
            remove
            {
                lock (this)
                    statusChanged -= value;
            }
        }

        #endregion

        public void PostDefaultStatus()
        {
            lock (this) {
                state.PostDefaultStatus();
            }
        }

        public void PostStatus(string status)
        {
            lock (this) {
                PostStatus(status, DefaultDuration);
            }
        }

        public void PostStatus(string status, double duration)
        {
            lock (this) {
                state.PostStatus(status, duration);
            }
        }

        private void Transition(IState sender, IState state)
        {
            lock (this) {
                Contract.NotNull(state, "state");
                if (sender != this.state)
                    return;
                if (state == this.state)
                    return;
                if (this.state != null)
                    this.state.Deactivate();
                this.state = state;
                this.state.Activate();
            }
        }

        private void OnStatusChanged()
        {
            //lock (this) {
            try {
                statusChanged(StatusText);
            } catch (ObjectDisposedException) {}
            //}
        }

        private event StatusTextEventHandler statusChanged = delegate { };

        #region Nested type: DefaultStatus

        private class DefaultStatus : IState
        {
            private readonly StatusTracker tracker;

            public DefaultStatus(StatusTracker tracker)
            {
                lock (this) {
                    this.tracker = Contract.NotNull(tracker, "tracker");
                }
            }

            #region IState Members

            public string StatusText
            {
                get
                {
                    lock (this) {
                        return tracker.DefaultStatusText;
                    }
                }
            }

            public void Activate()
            {
                DefaultStatusChanged();
            }

            public void Deactivate() {}

            public void DefaultStatusChanged()
            {
                lock (this) {
                    tracker.OnStatusChanged();
                }
            }

            public void PostDefaultStatus() {}

            public void PostStatus(string status, double duration)
            {
                lock (this) {
                    tracker.Transition(this, new MomentaryStatus(tracker, this, status, duration));
                }
            }

            #endregion
        }

        #endregion

        #region Nested type: IState

        private interface IState
        {
            string StatusText { get; }
            void Activate();
            void Deactivate();
            void DefaultStatusChanged();
            void PostDefaultStatus();
            void PostStatus(string status, double duration);
        }

        #endregion

        #region Nested type: MomentaryStatus

        private class MomentaryStatus : IState
        {
            private readonly double duration;
            private readonly IState parent;
            private readonly string status;
            private readonly Timer timer;
            private readonly StatusTracker tracker;
            private bool active;

            public MomentaryStatus(
                StatusTracker tracker, IState parent, string status, double duration)
            {
                lock (this) {
                    this.tracker = Contract.NotNull(tracker, "tracker");
                    this.parent = Contract.NotNull(parent, "parent");
                    this.status = Contract.NotEmpty(status, "status");
                    this.duration = Math.Max(0.0, duration);
                    timer = new Timer(duration*1000.0);
                    timer.AutoReset = false;
                    timer.Elapsed += Elapsed;
                }
            }

            #region IState Members

            public string StatusText
            {
                get
                {
                    lock (this) {
                        return status;
                    }
                }
            }

            public void Activate()
            {
                lock (this) {
                    active = true;
                    tracker.OnStatusChanged();
                    if (duration > 0.0)
                        timer.Start();
                }
            }

            public void Deactivate()
            {
                lock (this) {
                    active = false;
                    if (duration > 0.0)
                        timer.Stop();
                }
            }

            public void DefaultStatusChanged() {}

            public void PostDefaultStatus()
            {
                lock (this) {
                    tracker.Transition(this, parent);
                }
            }

            public void PostStatus(string status, double duration)
            {
                lock (this) {
                    tracker.Transition(this, new MomentaryStatus(tracker, parent, status, duration));
                }
            }

            #endregion

            private void Elapsed(object sender, ElapsedEventArgs e)
            {
                lock (this) {
                    if (active)
                        tracker.Transition(this, parent);
                }
            }
        }

        #endregion
    }
}